package net.roseboy.classfinal.util;

import a.a.a.A;

import java.io.*;
import java.util.*;
import java.util.concurrent.Executors;
import java.util.concurrent.ThreadPoolExecutor;
import java.util.zip.ZipEntry;
import java.util.zip.ZipFile;
import java.util.zip.ZipInputStream;
import java.util.zip.ZipOutputStream;

/**
 * jar/war操作工具类
 *
 * @author roseboy
 */
public class JarUtils {
    //打包时需要删除的文件
    public static final Set<String> DLE_FILES = new HashSet();
    static {
        DLE_FILES.add(".DS_Store");
        DLE_FILES.add("Thumbs.db");
    }

    /**
     * 把目录压缩成jar
     *
     * @param jarDir    需要打包的目录
     * @param targetJar 打包出的jar/war文件路径
     * @return 打包出的jar/war文件路径
     */
    public static String doJar(String jarDir, String targetJar) {
        File jarDirFile = new File(jarDir);
        //枚举jarDir下的所有文件以及目录
        List<File> files = new ArrayList<>();
        IoUtils.listFile(files, jarDirFile);

        ZipOutputStream zos = null;
        OutputStream out = null;

        try {
            File jar = new File(targetJar);
            if (jar.exists()) {
                jar.delete();
            }

            out = new FileOutputStream(jar);
            zos = new ZipOutputStream(out);

            for (File file : files) {
                if (isDel(file)) {
                    continue;
                }
                String fileName = file.getAbsolutePath().substring(jarDirFile.getAbsolutePath().length() + 1);
                fileName = fileName.replace(File.separator, "/");
                //目录，添加一个目录entry
                if (file.isDirectory()) {
                    ZipEntry ze = new ZipEntry(fileName + "/");
                    ze.setTime(System.currentTimeMillis());
                    zos.putNextEntry(ze);
                    zos.closeEntry();
                }
                //jar文件，需要写crc信息
                else if (fileName.endsWith(".jar")) {
                    byte[] bytes = IoUtils.readFileToByte(file);
                    ZipEntry ze = new ZipEntry(fileName);
                    ze.setMethod(ZipEntry.STORED);
                    ze.setSize(bytes.length);
                    ze.setTime(System.currentTimeMillis());
                    ze.setCrc(IoUtils.crc32(bytes));
                    zos.putNextEntry(ze);
                    zos.write(bytes);
                    zos.closeEntry();
                }
                //其他文件直接写入
                else {
                    ZipEntry ze = new ZipEntry(fileName);
                    ze.setTime(System.currentTimeMillis());
                    zos.putNextEntry(ze);
                    byte[] bytes = IoUtils.readFileToByte(file);
                    zos.write(bytes);
                    zos.closeEntry();
                }
            }
        } catch (Exception e) {
            e.printStackTrace();
        } finally {
            IoUtils.close(zos, out);
        }
        return targetJar;
    }

    /**
     * 把目录压缩成jar
     *
     * @param content    需要打包的目录
     * @param targetJar 打包出的jar/war文件路径
     * @return 打包出的jar/war文件路径
     */
    public static String doJar(Map<String,byte[]> content, String targetJar) {

        OutputStream out = null;

        try {
            File jar = new File(targetJar);
            if (jar.exists()) {
                jar.delete();
            }
            IoUtils.writeFile(jar,doJar(content));

        } catch (Exception e) {
            e.printStackTrace();
        } finally {
            IoUtils.close(out);
        }
        return targetJar;
    }
    /**
     * 把目录压缩成jar
     *
     * @param content    需要打包的目录
     * @return 打包出的jar/war文件路径
     */
    public static byte[] doJar(Map<String,byte[]> content) {

        ByteArrayOutputStream out = new ByteArrayOutputStream();

        try {

           ZipOutputStream zos = new ZipOutputStream(out);
            for (Map.Entry<String, byte[]> stringEntry : content.entrySet()) {
                String path=stringEntry.getKey();
                byte[] bs=stringEntry.getValue();
                try {
                    if(bs==null){
                        ZipEntry ze = new ZipEntry(path+"/");
                        ze.setTime(System.currentTimeMillis());
                        zos.putNextEntry(ze);
                        zos.closeEntry();
                    }else if (path.endsWith(".jar")) {
                        ZipEntry ze = new ZipEntry(path);
                        ze.setMethod(ZipEntry.STORED);
                        ze.setSize(bs.length);
                        ze.setTime(System.currentTimeMillis());
                        ze.setCrc(IoUtils.crc32(bs));
                        zos.putNextEntry(ze);
                        zos.write(bs);
                        zos.closeEntry();
                    }else{
                        ZipEntry ze = new ZipEntry(path);
                        ze.setTime(System.currentTimeMillis());
                        zos.putNextEntry(ze);
                        zos.write(bs);
                        zos.closeEntry();

                    }
                }catch (Exception e){

                }
            }

            zos.close();
        } catch (Exception e) {
            e.printStackTrace();
        } finally {
            IoUtils.close(out);
        }
        return out.toByteArray();
    }


    /**
     * 释放jar内以及子jar的所有文件
     *
     * @param jarPath   jar文件
     * @param targetDir 释放文件夹
     * @return 所有文件的完整路径，包含目录
     */
    public static List<String> unJar(String jarPath, String targetDir) {
        return unJar(jarPath, targetDir, null);
    }

    /**
     * 释放jar内以及子jar的所有文件
     *
     * @param jarPath   jar文件
     * @param targetDir 释放文件夹
     * @return 所有文件的完整路径，包含目录
     */
    public static List<String> unJar(String jarPath, String targetDir, List<String> includeFiles) {
        final List<String> list = new ArrayList<>();
        File target = new File(targetDir);
        if (!target.exists()) {
            target.mkdirs();
        }

        FileInputStream fin = null;
        ZipFile zipFile = null;
        try {

            long time = System.currentTimeMillis();
            zipFile = new ZipFile(new File(jarPath));
            ZipEntry entry;
            File targetFile;
            //先把文件夹创建出来
            Enumeration<?> entries = zipFile.entries();
            int count=0;
            while (entries.hasMoreElements()) {
                count++;
                entry = (ZipEntry) entries.nextElement();
                if (entry.isDirectory()) {
                    targetFile = new File(target, entry.getName());
                    if(!targetFile.exists()){
                        targetFile.mkdirs();
                    }
                } else {//有时候entries没有目录,根据文件路径创建目录
                    int lastSeparatorIndex = entry.getName().lastIndexOf("/");
                    if (lastSeparatorIndex > 0) {
                        targetFile = new File(target, entry.getName().substring(0, lastSeparatorIndex));
                        if (!targetFile.exists()) {
                            targetFile.mkdirs();
                        }
                    }
                }
            }

            System.out.println("创建全部文件夹:"+(System.currentTimeMillis()-time));
            time = System.currentTimeMillis();
            //再释放文件
            entries = zipFile.entries();
            ThreadPoolExecutor service = (ThreadPoolExecutor) Executors.newFixedThreadPool(20);
            while (entries.hasMoreElements()) {
                entry = (ZipEntry) entries.nextElement();
                if (entry.isDirectory()) {
                    continue;
                }
                targetFile = new File(target, entry.getName());

                //跳过未包含的文件
                if (includeFiles != null && includeFiles.size() > 0 && !includeFiles.contains(targetFile.getName())) {
                    continue;
                }
//                byte[] bytes = IoUtils.toBytes(zipFile.getInputStream(entry));
//                IoUtils.writeFile(targetFile, bytes);
                final File finalTargetFile = targetFile;
                final ZipFile finalZipFile = zipFile;
                final ZipEntry finalEntry = entry;
                service.execute(new Runnable() {
                    @Override
                    public void run() {
                        try {
//                        semaphore.acquire();
                            IoUtils.writeFile(finalTargetFile, finalZipFile.getInputStream(finalEntry));
                            list.add(finalTargetFile.getAbsolutePath());
                        } catch (IOException e) {
                            e.printStackTrace();
                        }finally {
//                        semaphore.release();
                        }
                    }
                });
            }
            while (service.getActiveCount()>0){
                Thread.sleep(10);
            }

            System.out.println("写入全部文件:"+(System.currentTimeMillis()-time));
            System.out.println("写入全部文件:"+(list.size()));
            time = System.currentTimeMillis();
        } catch (Exception e) {
            e.printStackTrace();
        } finally {
            IoUtils.close(zipFile, fin);
        }
        return list;
    }


    /**
     * 释放jar内以及子jar的所有文件
     *
     * @param jarPath   jar文件
     * @param includeFiles 指定文件列表
     * @return 所有文件的完整路径，包含目录
     */
    public static Map<String,byte[]> unZipToMap(String jarPath,  List<String> includeFiles) {
        FileInputStream fin = null;
        ZipFile zipFile = null;
        Map<String,byte[]> map=new LinkedHashMap<>();
        try {

            long time = System.currentTimeMillis();
            zipFile = new ZipFile(new File(jarPath));
            ZipEntry entry;
            Enumeration<?> entries = zipFile.entries();

            while (entries.hasMoreElements()) {
                entry = (ZipEntry) entries.nextElement();
                if (entry.isDirectory()) {
                    if(entry.getName().endsWith("/")){
                        map.put(entry.getName().substring(0,entry.getName().length()-1),null);
                    }else{
                        map.put(entry.getName(),null);
                    }

                } else {//有时候entries没有目录,根据文件路径创建目录
                    int lastSeparatorIndex = entry.getName().lastIndexOf("/");
                    if (lastSeparatorIndex > 0) {
                        map.put(entry.getName().substring(0, lastSeparatorIndex),null);
                    }
                    String[] names=entry.getName().split("/|\\\\");
                    String fileName=names[names.length-1];
                    //跳过未包含的文件
                    if (includeFiles != null && includeFiles.size() > 0 && !includeFiles.contains(fileName)) {
                        continue;
                    }
                    if(DLE_FILES.contains(fileName))continue;
                    map.put(entry.getName(),IoUtils.toBytes(zipFile.getInputStream(entry)));
                }
            }

            System.out.println("加载全部文件:"+(System.currentTimeMillis()-time));
            System.out.println("加载文件数量:"+(map.size()));
            time = System.currentTimeMillis();
        } catch (Exception e) {
            e.printStackTrace();
        } finally {
            IoUtils.close(zipFile, fin);
        }
        return map;
    }

    /**
     * 释放jar内以及子jar的所有文件
     *
     * @param in   jar文件
     * @param includeFiles 指定文件列表
     * @return 所有文件的完整路径，包含目录
     */
    public static Map<String,byte[]> unZipToMap(byte[] in,  List<String> includeFiles) {
        FileInputStream fin = null;
        ZipInputStream zipInputStream = null;
        Map<String,byte[]> map=new HashMap<>();
        try {

            long time = System.currentTimeMillis();
            zipInputStream = new ZipInputStream(new ByteArrayInputStream(in));
            ZipEntry entry;


            while ((entry=zipInputStream.getNextEntry())!=null) {
                if (entry.isDirectory()) {
                    map.put(entry.getName(),null);
                } else {//有时候entries没有目录,根据文件路径创建目录
                    int lastSeparatorIndex = entry.getName().lastIndexOf("/");
                    if (lastSeparatorIndex > 0) {
                        map.put(entry.getName().substring(0, lastSeparatorIndex),null);
                    }
                    map.put(entry.getName(),null);
                    String[] names=entry.getName().split("/|\\\\");
                    String fileName=names[names.length-1];
                    //跳过未包含的文件
                    if (includeFiles != null && includeFiles.size() > 0 && !includeFiles.contains(fileName)) {
                        continue;
                    }
                    map.put(entry.getName(),IoUtils.toBytesWithoutCLose(zipInputStream));
                }
            }

            System.out.println("加载全部文件:"+(System.currentTimeMillis()-time));
            System.out.println("加载文件数量:"+(map.size()));
            time = System.currentTimeMillis();
        } catch (Exception e) {
            e.printStackTrace();
        } finally {
            IoUtils.close(zipInputStream, fin);
        }
        return map;
    }

    /**
     * 在jar中根据文件名释放文件
     *
     * @param zip        压缩文件
     * @param fileName   文件名
     * @param targetFile 释放的目标文件
     * @return 释放出的文件的绝对路径
     */
    public static String releaseFileFromJar(File zip, String fileName, File targetFile) {
        byte[] bytes = getFileFromJar(zip, fileName);
        if (bytes == null) {
            return null;
        }
        IoUtils.writeFile(targetFile, bytes);
        return targetFile.getAbsolutePath();

    }

    /**
     * 在压缩文件中获取一个文件的字节
     *
     * @param zip      压缩文件
     * @param fileName 文件名
     * @return 文件的字节
     */
    public static byte[] getFileFromJar(File zip, String fileName) {
        ZipFile zipFile = null;
        try {
            if (!zip.exists()) {
                return null;
            }
            zipFile = new ZipFile(zip);
            ZipEntry zipEntry = zipFile.getEntry(fileName);
            if (zipEntry == null) {
                return null;
            }
            InputStream is = zipFile.getInputStream(zipEntry);
            return IoUtils.toBytes(is);
        } catch (IOException e) {
            e.printStackTrace();
        } finally {
            IoUtils.close(zipFile);
        }
        return null;
    }

    /**
     * 是否删除这个文件
     *
     * @param file 文件
     * @return 是否需要删除
     */
    public static boolean isDel(File file) {
        for (String f : DLE_FILES) {
            if (file.getAbsolutePath().endsWith(f)) {
                return true;
            }
        }
        return false;
    }

    /**
     * 获取class运行的classes目录或所在的jar包目录
     *
     * @return 路径字符串
     */
    public static String getRootPath(String path) {
        if (path == null) {
            path = A.getRoot();
        }

        try {
            path = java.net.URLDecoder.decode(path, "utf-8");
        } catch (UnsupportedEncodingException e) {
        }

        if (path.startsWith("jar:") || path.startsWith("war:")) {
            path = path.substring(4);
        }
        if (path.startsWith("file:")) {
            path = path.substring(5);
        }

        //没解压的war包
        if (path.contains("*")) {
            return path.substring(0, path.indexOf("*"));
        }
        //war包解压后的WEB-INF
        else if (path.contains("WEB-INF")) {
            return path.substring(0, path.indexOf("WEB-INF"));
        }
        //jar
        else if (path.contains("!")) {
            return path.substring(0, path.indexOf("!"));
        }
        //普通jar/war
        else if (path.endsWith(".jar") || path.endsWith(".war")) {
            return path;
        }
        //no
        else if (path.contains("/classes/")) {
            return path.substring(0, path.indexOf("/classes/") + 9);
        }
        return null;
    }

}
